package util;

import entities.Player;
import listener.MessageListener;
import org.json.JSONArray;
import org.json.JSONObject;

import java.awt.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
import java.util.List;

/***
 * Author: Mr.Zhao, Time: 2018/5/5/10:50
 * Description:
 */
public class MyServices {
    /* 在线的客户端集合 */
    //public static List<Socket> clients = new ArrayList<Socket>();

    public static List<Player> players = new ArrayList<>();

    /* 已配对的客户端，默认键代表红方，值代表黑方 */
    //public static Map<Socket, Socket> clientsMap = new HashMap<Socket, Socket>();

    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(MessageListener.REMOTE_PORT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("服务器端--开始监听");

        while (true) {
            //接收客户端，建立连接
            Socket socket = null;
            try {
                socket = serverSocket.accept();
                //clients.add(socket);
                Player player = new Player(socket);
                players.add(player);
                //ServiceHandle sh = new ServiceHandle(socket);
                ServiceHandle sh = new ServiceHandle(player);

                Thread t = new Thread(sh);
                t.start();
            } catch (IOException e) {
                e.printStackTrace();
            }


        }
    }


    /**
     * 监听并处理一个玩家的所有请求
     */
    private static class ServiceHandle implements Runnable {
        private Player player;

        public ServiceHandle(Player player) {
            this.player = player;
        }

        public void run() {
            while (true) {

                try {
                    /*两种判断玩家是否离线的方式，一是发送，二是接收。
                        经测试，直接运行jar包时，如果玩家退出，只有发送数据才能判断是否掉线。
                        而从IDEA中运行时，如果玩家退出，只有接收数据才能判断是否掉线*/
                    player.getSocket().sendUrgentData(0xFF);
                    BufferedReader reader = new BufferedReader(
                            new InputStreamReader(player.getSocket().getInputStream(), "UTF-8"));
                    String str;
                    long count = 0;
                    while ((str = reader.readLine()) != null) {
                        count++;
                        //第一次接收到心跳包时才会提醒并返回一个玩家的信息
                        if (str.equals(MessageListener.HEARTBEAT) && count == 1) {
                            System.out.println(String.format("玩家%d已上线，玩家信息：%s\t", players.indexOf(player),
                                    player.getFreeInfo()));

                            JSONObject json = new JSONObject();
                            json.put("flag", "我的信息");
                            json.put("myInfo", new JSONObject(player.toString()));
                            sendMsg2Player(player, json);
                            continue;
                        }
                        if (!str.equals(MessageListener.HEARTBEAT)) {
                            System.out.println(String.format("客户端%d发送信息：%s", players.indexOf(player), str));
                            dispatchMessage(str);
                        }
                    }
                } catch (IOException e) {
                    playerOffLine(player);
                    break;
                }
            }
        }

        /**
         * 如果玩家已离线，向已配对的玩家发送消息，并从列表中移除该玩家，关闭该玩家的连接
         *
         * @param player
         */
        private void playerOffLine(Player player) {
            System.out.println(String.format("玩家%d已掉线，玩家信息：\t", players.indexOf(player))
                    + player.toString());
            players.remove(player);
            if (player.getOpponent() != null) {
                JSONObject json = new JSONObject();
                json.put("flag", "对手离线");
                player.getOpponent().setOpponent(null);
                sendMsg2Player(player.getOpponent(), json);
                System.out.println(json);
            }
            try {
                player.getSocket().close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }

        /**
         * 将客户端传来的消息进行分发
         *
         * @param jsonStr 传进来的字符串只能为Json
         */
        private void dispatchMessage(String jsonStr) {
            JSONObject jsonObject = new JSONObject(jsonStr);
            String flag = jsonObject.getString("flag");

            if (flag.equals("列出在线玩家")) {
                listPlayers();
            }

            if (flag.equals("发起游戏邀请")) {
                String toId = jsonObject.getString("to");
                for (Player player : players) {
                    if (player.getId().equals(toId)) {
                        if (player.isBusy()) {
                            return;
                        }
                        JSONObject json = new JSONObject();
                        json.put("flag", "收到游戏邀请");
                        json.put("playerInfo", new JSONObject(this.player.toString()));
                        sendMsg2Player(player, json);
                        //暂时设置为对手，如果被拒绝则再设为null
                        player.setOpponent(this.player);
                        this.player.setOpponent(player);
                        break;
                    }
                }
            }

            if (flag.equals("接受游戏邀请")) {
                String toId = jsonObject.getString("to");
                for (Player player : players) {
                    if (player.getId().equals(toId)) {
                        pairPlayers(player, this.player);
                        break;
                    }
                }
            }

            if (flag.equals("拒绝游戏邀请")) {
                String toId = jsonObject.getString("to");
                for (Player player : players) {
                    if (player.getId().equals(toId)) {
                        sendMsg2Player(player, jsonObject);
                        this.player.setOpponent(null);
                        player.setOpponent(null);
                        break;
                    }
                }
            }

            //走棋和聊天，直接将内容进行转发
            if (flag.equals("走棋") || flag.equals("聊天")) {
                if (player.getOpponent() == null) {
                    JSONObject json = new JSONObject();
                    json.put("flag", "对手已离线");
                    sendMsg2Player(player, json);
                    return;
                }
                sendMsg2Player(player.getOpponent(), jsonObject);
            }

            if (flag.equals("游戏结束")) {
                player.setOpponent(null);
            }

        }

        /**
         * 列出所有的在线玩家（不包含自己），将数据返回给玩家
         */
        private void listPlayers() {
            System.out.println("正在执行listPlayers方法");

            JSONObject jsonObject = new JSONObject();
            jsonObject.put("to", player.getId());
            jsonObject.put("flag", "在线玩家列表");

            ArrayList<Player> tempPlayers = new ArrayList<Player>();
            for (Player player : players) {
                if (player.equals(this.player)) {
                    continue;
                }
                tempPlayers.add(player);
            }
            JSONArray jsonArray = new JSONArray(tempPlayers.toString());
            jsonObject.put("onlinePlayers", jsonArray);
            sendMsg2Player(player, jsonObject);
        }

        /**
         * 发送信息给玩家
         *
         * @param receiver   要发送给的玩家
         * @param jsonObject 消息类型必须为json
         */
        public void sendMsg2Player(Player receiver, JSONObject jsonObject) {
            try {
                PrintWriter printWriter = new PrintWriter(receiver.getSocket().getOutputStream(), true);
                printWriter.println(jsonObject);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


        /**
         * 配对玩家
         *
         * @param player1
         * @param player2
         */
        private void pairPlayers(Player player1, Player player2) {
            if (Math.random() > 0.5) {
                player1.setColor(Color.RED);
                player2.setColor(Color.BLACK);
            } else {
                player1.setColor(Color.BLACK);
                player2.setColor(Color.RED);
            }
            player1.setOpponent(player2);
            player2.setOpponent(player1);
            JSONObject json1 = new JSONObject();
            json1.put("flag", "游戏开始");
            json1.put("myInfo", new JSONObject(player1.toString()));

            JSONObject json2 = new JSONObject();
            json2.put("flag", "游戏开始");
            json2.put("myInfo", new JSONObject(player2.toString()));

            sendMsg2Player(player1, json1);
            sendMsg2Player(player2, json2);
        }

    }
}
